//package datamining;

import java.io.*;
import java.text.DecimalFormat;
import java.util.*;

public class FindOutRules {

    public double min_conf = 0;
    public boolean flag = true;
    public HashMap<String, String> nameHm;
    public HashMap<String, Double[]> hmap = new HashMap<String, Double[]>();
//constructor function of class FindOutRules
    public FindOutRules(HashMap<String, String> nameHm, double min_conf) {
        this.nameHm = nameHm;
        this.min_conf = min_conf;
    }
//generate all the association rule from the dataset and write into output.txt
    public void AssociationRules(HashMap<String, Integer> frqmap, int total) {
        //for(int i=0; i<frqmap.size(); i++)
        //{
        Set data = frqmap.entrySet();
        Iterator i = data.iterator();

        while (i.hasNext()) {
            // System.out.println("Processing...");
            Map.Entry me = (Map.Entry) i.next();
            GenRules(me.getKey().toString(), frqmap, total);
        }

        //    GenRules(frqmap.get(i).toString(), frqmap);
        //}
        OutputRules();
    }
//generate rules with confidence greater than min_conf
    public void GenRules(String item, HashMap<String, Integer> frqmap, int total) {
        double rule_conf, rule_supp;
        List<String> tmp = new ArrayList<String>();
        int length = item.length();
        //System.out.println(length);
        if (length > 1) {
            tmp = GenCombination(item);
            for (int i = 0; i < tmp.size(); i++) {
                //System.out.println("running...");
                rule_conf = (double) (frqmap.get(item)) / (frqmap.get(tmp.get(i)));
                rule_supp = (double) (frqmap.get(item)) / total;
                //System.out.println(rule_conf);
                if (rule_conf >= min_conf) {
                    //WriteRules(tmp.get(i).toString(), item, rule_conf);
                    StoreRules(tmp.get(i).toString(), item, rule_conf, rule_supp);
                }
            }
        }
    }
//generate all the proper subset of a given set
    public List<String> GenCombination(String item) {
        List<String> frqlist = new ArrayList<String>();

        if (item.length() == 3) {
            String[] tmp = item.split(" ");
            frqlist.add(tmp[0]);
            frqlist.add(tmp[1]);
        } else {
            String[] tmp = item.split(" ");
            Set<Set<String>> result = new HashSet<Set<String>>();
            Set<String> frq_set = new HashSet<String>();
            for (int i = 0; i < tmp.length; i++) {
                frq_set.add(tmp[i]);
            }
            for (int i = 1; i < tmp.length; i++) {
                result = SubsetCombin.getCombin(i, frq_set);
                Iterator j = result.iterator();
                while (j.hasNext()) {
                    Set proitem = (Set) j.next();
                    String proset = SetToString(proitem);
                    frqlist.add(proset);
                }

            }
        }
        return frqlist;
    }
//helper function translate a set data structure to string
    public String SetToString(Set<String> proset) {
        String result = "";
        List<Integer> list = new ArrayList<Integer>();
        Iterator i = proset.iterator();
        while (i.hasNext()) {
            String buf = (String) i.next();
            int tmp = Integer.parseInt(buf);
            list.add(tmp);

        }
        Collections.sort(list);
        for (int j = 0; j < list.size(); j++) {
            result = result + list.get(j).toString() + " ";
        }
        //System.out.println("old: "+result);
        result = result.substring(0, result.length() - 1);
        //System.out.println("new: "+result);
        return result;
    }
//store a valid rule with conf_val and supp_val into a hashmap
    public void StoreRules(String subset, String frqset, double rule_conf, double rule_supp) {

        //String[] sol = frqset.split(subset);
        //System.out.println(sol);
        String result = "";
        List<Integer> tmp1 = new ArrayList<Integer>();
        List<Integer> tmp2 = new ArrayList<Integer>();
        Double[] stat = new Double[2];
        String[] sub = subset.split(" ");
        String[] fset = frqset.split(" ");
        for (int i = 0; i < sub.length; i++) {
            tmp1.add(Integer.parseInt(sub[i]));
        }
        for (int j = 0; j < fset.length; j++) {
            tmp2.add(Integer.parseInt(fset[j]));
        }
        for (int i = 0; i < tmp1.size(); i++) {
            if (tmp2.contains(tmp1.get(i))) {
                tmp2.remove(tmp1.get(i));
            }
        }
        String sol = "";
        for (int i = 0; i < tmp2.size(); i++) {
            sol = sol + tmp2.get(i).toString() + " ";
        }
        sol = sol.trim();

        //result = "["+subset+"] => ["+ sol +"]";
        result = MapToRecord(subset, sol);
        stat[0] = rule_conf;
        stat[1] = rule_supp;
        hmap.put(result, stat);
        //System.out.println(result+"(Conf: "+rule_conf+ ", Supp: "+rule_supp+")");

    }
//helper function
    public String MapToRecord(String subset, String sol) {
        String[] tmp1 = subset.split(" ");
        String[] tmp2 = sol.split(" ");
        String newsetlf = "";
        String newsetrt = "";
        String result = "";
        for (int i = 0; i < tmp1.length; i++) {
            int buf = Integer.parseInt(tmp1[i]);
            newsetlf = newsetlf + nameHm.get(String.valueOf(buf)).toString() + ", ";
        }
        newsetlf = newsetlf.substring(0, newsetlf.length() - 2);
        for (int i = 0; i < tmp2.length; i++) {
            int buf = Integer.parseInt(tmp2[i]);
            newsetrt = newsetrt + nameHm.get(String.valueOf(buf)).toString() + ", ";
        }
        newsetrt = newsetrt.substring(0, newsetrt.length() - 2);

        result = "[" + newsetlf + "] => [" + newsetrt + "]";

        return result;
    }
//insert sort function for confidence value sorting
    public void insertsort(List<List<String>> data) {
        List<String> temp;
        for (int i = 0; i < data.size(); i++) {
            for (int j = data.size() - 1; j > i; j--) {
                if (Double.parseDouble(data.get(j).get(1)) < Double.parseDouble(data.get(j - 1).get(1))) {
                    temp = data.get(j);
                    data.set(j, data.get(j - 1));
                    data.set(j - 1, temp);
                }
            }
        }
    }
//output all the rules into a file
    public void OutputRules() {
        List<List<String>> temp = new ArrayList<List<String>>();
        List<String> node = new ArrayList<String>();
        Set data = hmap.entrySet();
        Iterator i = data.iterator();
        String outputFile = "Output.txt";
        //FileWriter fw = new FileWriter(outputFile, true);

        //int count=0;
        while (i.hasNext()) {
            Map.Entry me = (Map.Entry) i.next();
            node = new ArrayList<String>();
            node.add(me.getKey().toString());
            Double[] d = (Double[]) me.getValue();
            node.add(d[0].toString());
            node.add(d[1].toString());
            //node.add(me.getValue());
            temp.add(node);
        }

        this.insertsort(temp);
        try {
            DecimalFormat df = new DecimalFormat();
            df.setMaximumFractionDigits(2);
            FileWriter fw = new FileWriter(outputFile, true);
            BufferedWriter file_out = new BufferedWriter(fw);
            file_out.write("==High-confidence association rules (min_conf=" + min_conf * 100 + "%) \n");
            System.out.println("==High-confidence association rules (min_conf=" + min_conf * 100 + "%) ");
            for (int j = temp.size() - 1; j >= 0; j--) {
                file_out.write(temp.get(j).get(0) + "(Conf: " + df.format(Double.parseDouble(temp.get(j).get(1)) * 100) + "%, Supp: " + df.format(Double.parseDouble(temp.get(j).get(2)) * 100) + "%)\n");
                System.out.println(temp.get(j).get(0) + "(Conf: " + df.format(Double.parseDouble(temp.get(j).get(1)) * 100) + "%, Supp: " + df.format(Double.parseDouble(temp.get(j).get(2)) * 100) + "%)");
            }
            file_out.close();

        } catch (IOException e) {
            System.out.println(e);
        }
    }
}
